package com.hqd.jjvm.instructions;

import com.hqd.jjvm.instructions.change.TypeChangeInstruction;
import com.hqd.jjvm.instructions.compare.CompareInstruction;
import com.hqd.jjvm.instructions.constants.ConstInstruction;
import com.hqd.jjvm.instructions.ctrl.CtrlInstruction;
import com.hqd.jjvm.instructions.extend.ExtendInstruction;
import com.hqd.jjvm.instructions.load.LoadInstruction;
import com.hqd.jjvm.instructions.math.MathInstruction;
import com.hqd.jjvm.instructions.ref.RefInstruction;
import com.hqd.jjvm.instructions.reserve.ReserveInstruction;
import com.hqd.jjvm.instructions.stack.StackInstruction;
import com.hqd.jjvm.instructions.store.StoreInstruction;
import com.hqd.jjvm.rtda.JThread;
import com.hqd.jjvm.rtda.heap.JMethod;
import com.hqd.jjvm.util.HexStrTransformUtil;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

public class InstructionContext {

    private JMethod jMethod;
    private JThread jThread;
    static volatile int count = 0;

    public InstructionContext(JMethod jMethod) {
        this.jMethod = jMethod;
        this.jThread = new JThread(jMethod);
    }

    private Integer parseAndReadU1(String codeStr) {
        AtomicInteger offer = jThread.getNextPc();
        return HexStrTransformUtil.parseHexToInt(codeStr.substring(offer.get() << 1, (offer.get() << 1) + 2));
    }

    public void invoke() {
        AbstractInstruction instruction = null;
        String codeStr = "";
        while (true) {
            codeStr = jThread.getJvmStack().getTop().getJMethod().getCode().getHexStr();
            Integer instructionCode = parseAndReadU1(codeStr);
            InstructionType instructionType = InstructionType.getType(instructionCode);
            InstructionClassifyType classifyType = InstructionClassifyType.getType(instructionCode);
            count++;
            //System.out.println(count + ":jclass:" + jThread.getJvmStack().getTop().getJMethod().getJClass().getClassName() + ":" + classifyType + ":" + jThread.getNextPc() + ":" + jThread.getJvmStack().getTop().getJMethod().getName());
            switch (Objects.requireNonNull(classifyType)) {
                case CONST:
                    instruction = new ConstInstruction(jThread);
                    break;
                case LOAD:
                    instruction = new LoadInstruction(jThread);
                    break;
                case STORE:
                    instruction = new StoreInstruction(jThread);
                    break;
                case STACK:
                    instruction = new StackInstruction(jThread);
                    break;
                case MATH:
                    instruction = new MathInstruction(jThread);
                    break;
                case CHANGE:
                    instruction = new TypeChangeInstruction(jThread);
                    break;
                case COMPARE:
                    instruction = new CompareInstruction(jThread);
                    break;
                case CONTROL:
                    instruction = new CtrlInstruction(jThread);
                    break;
                case REF:
                    instruction = new RefInstruction(jThread);
                    break;
                case EXTEND:
                    instruction = new ExtendInstruction(jThread);
                    break;
                case RESERVE:
                    instruction = new ReserveInstruction(jThread);
                    break;
                default:
                    throw new VerifyError("Bad instruction:" + instructionCode);

            }
            if (count == 26743) {
                //System.out.println("ss");
            }
            instruction.execute(instructionType);
            if (!instruction.getIsRun().get()) {
                break;
            }
            instruction.offerIncrement();

        }
    }
}
